/*
 * @Author: xiaosihan
 * @Date: 2021-07-31 22:09:05
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-10-23 00:57:39
 */

import { fabric } from "fabric";
import _ from "lodash";
import * as THREE from "three";
import { GLTF } from "three/examples/jsm/loaders/GLTFLoader";
import uvJPG from "./uv.jpg";
import yifuGLB from "./yifu.glb";


// 衣服
export default new class Clothes extends THREE.Object3D {

    constructor() {
        super();

        (async () => {
            await this.init();

            this.addImage(uvJPG);
        })();
    }

    texture!: THREE.Texture;

    fabricCanvas!: fabric.Canvas;

    initFabricCanvas(id: string) {
        this.fabricCanvas = new fabric.Canvas(id, {
            // backgroundColor: "#ccc"
        });

        this.fabricCanvas.on("before:render", () => {
            if (this.texture) {
                this.texture.needsUpdate = true;
            }
        });
    }

    addImage(url: string) {
        fabric.Image.fromURL(url, (img) => {
            img.scale(0.1).set({
                left: 0,
                top: 0,
                angle: 0,
            });
            this.fabricCanvas.add(img);
        });
    }

    planMesh?: THREE.Mesh;

    async init() {

        const { DRACOLoader } = await import("three/examples/jsm/loaders/DRACOLoader");
        const { GLTFLoader } = await import("three/examples/jsm/loaders/GLTFLoader");

        while (!this.fabricCanvas) {
            await new Promise(resolve => setTimeout(resolve, 200));
        }

        this.texture = new THREE.CanvasTexture(this.fabricCanvas.getElement());

        this.texture.center.set(0.5, 0.5);

        const dracoLoader = new DRACOLoader();

        dracoLoader.setDecoderPath("/src/asset/");

        var loader = new GLTFLoader();
        loader.setDRACOLoader(dracoLoader);

        loader.load(yifuGLB, async (gltf: GLTF) => {

            this.add(gltf.scene);

            // 计算模型的尺寸
            let min = new THREE.Vector3();
            let max = new THREE.Vector3();

            // 更具模型的情况 把正面朝向 z 的正半轴
            gltf.scene.rotateY(Math.PI);

            gltf.scene.traverse(object => {
                if (_.get(object, ["isMesh"])) {
                    (object as THREE.Mesh<THREE.BufferGeometry, THREE.Material>).material.side = THREE.DoubleSide;
                    (object as THREE.Mesh<THREE.BufferGeometry, THREE.Material>).geometry.computeBoundingBox();
                    min.min((object as THREE.Mesh<THREE.BufferGeometry, THREE.Material>).geometry.boundingBox!.min);
                    max.max((object as THREE.Mesh<THREE.BufferGeometry, THREE.Material>).geometry.boundingBox!.max);
                }
            });

            // 创建一个包围盒
            let box = new THREE.Box3(min, max);
            let box3Helper = new THREE.Box3Helper(box, new THREE.Color("#f00"));
            this.add(box3Helper);


            // 创建一个可以贴图的面
            this.planMesh = new THREE.Mesh(
                new THREE.PlaneBufferGeometry(max.x - min.x, max.y - min.y, 100, 100),
                new THREE.MeshLambertMaterial({
                    side: THREE.DoubleSide,
                    alphaTest: 0.01,
                    map: this.texture,
                })
            );

            // this.planMesh.position.setZ(max.z + 1);
            this.planMesh.position.setZ(0.04);

            this.add(this.planMesh);

            // 创建一条射线来探测模型的表面形状
            let raycaster = new THREE.Raycaster();

            let positionBufferAttribute = this.planMesh.geometry.attributes.position;

            for (let i = 0; i < positionBufferAttribute.count; i++) {
                const x = positionBufferAttribute.getX(i);
                const y = positionBufferAttribute.getY(i);
                // const z = positionBufferAttribute.getZ(i);

                raycaster.ray.origin.set(x, y, max.z);

                await new Promise(resolve => requestAnimationFrame(resolve));

                let interscetion = raycaster.intersectObjects([gltf.scene], true);

                if (interscetion.length) {
                    positionBufferAttribute.setZ(i, Math.max(0, max.z - interscetion[0].distance));
                    positionBufferAttribute.needsUpdate = true;
                }
            }


        });
    }


}
